/**
 * KeyboardMapper项目<br /><br />      
 *
 *	一、希望实现的功能<br />
 *		通过用户的自定义，使得浏览器支持更多的快捷键<br /><br />
 *	
 *	二、需求分析：<br />
 *		1、禁用快捷键，要有全部的和指定快捷键的禁用	<br />	
 *		2、恢复快捷键<br />
 *		3、绑定快捷键<br />
 *		4、移除快捷键<br />
 *		5、快捷键作用范围<br />
 *		6、查询已经绑定的快捷键<br />
 *		7、组合快捷键<br />
 *		8、修改快捷键<br />
 *	三、概要设计<br />
 *		1、快捷键映射：在这里定义所有的快捷键组合<br />
 *			var KeyMaps = [];<br /><br />
 *		
 *		2、API设定：根据需求分析，制定一系列基本的接口方法<br />
 *			//禁用快捷键<br />
 *	        //不传参数就处理所有快捷键，传一个ID就处理一个， 传多个就处理多个，类似jQuery那种<br />
 *			public boolean disableShortcuts();<br />
 *			//恢复快捷键<br />
 *	        public boolean restoreShortcuts();<br /><br />
 *	
 *			//绑定快捷键<br />
 *			public boolean addShortcuts();<br />
 *			//移除快捷键<br />
 *			public boolean removeShortcuts();<br />
 *			//修改快捷键<br />
 *			public boolean alterShortcuts();<br /><br />
 *	
 *			//查询已经绑定的快捷键(指定)<br />
 *			public Array findShortcuts(KeyMaps _keyID);<br />
 *
 * @class 自定义快捷键支持
 * @author <b>wang.hongxu(王宏旭),zhao.xianlie(赵先烈)</b><br /><br />
 * @version kbm-1.0.0.1    
 * @constructor 
 * @return  KeyboardMapper实例     
 */
var KeyboardMapper = function(){
	//目前内容为空，后续可追加
};

/**
 * 属性合集
 */
(function(){
	/**
	 * 项目名称
	 * @type String
	 */
	KeyboardMapper.prjName = "KeyboardMapper";
	/**
	 * 版本信息
	 * @type String
	 */
	KeyboardMapper.version = "kbm-1.0.0";
	/**
	 * 作者
	 * @type String
	 */
	KeyboardMapper.author = ["wang.hongxu(王宏旭)","zhao.xianlie(赵先烈)"];
})();

/**
 * 操作方法合集
 */
(function(){
	/**
	 * KeyboardMapper类的静态属性：快捷键映射表(键均为大写字母)，默认为空数组
	 * @type Array 
	 * @example
	 * [
	 * 	 {"key":"CTRL+A","fn":fnCallback1,"usability":true},
	 * 	 {"key":"CTRL+B",'fn':fnCallback2,"usability":true}
	 * ]
	 * 
	 */
	KeyboardMapper.KeyMaps = [];
	
	/**
	 * 绑定快捷键<br />
	 * @param {String/Array} arrShortcuts  需要注册的快捷键，可以是字符串，如"CTRL+B"，<br />
	 * 				  也可以是数组，如["CTRL+B","ALT+CTRL+E"]<br />
	 * @param {function} fnCallback  回调函数<br />
	 * @param {json/object} optConfig 可选的配置参数，配置项包括如下：<br />
	 * 		{<br />
	 * 		&nbsp;&nbsp;	target : document	//可以是DOM节点的ID，默认是document<br />
	 * 		}<br />
	 * @return {object} KeyboardMapper本身
	 * @example
	 * //为整个文档区域注册一个Ctrl+X快捷键
	 * KeyboardMapper.addShortcuts("Ctrl+x",function(){
	 * 	alert("Ctrl+X 在document上触发！");
	 * });
	 * @example
	 * //为文本框txtKBM01绑定一个快捷键
	 * KeyboardMapper.addShortcuts("Ctrl+S",function(){
	 *		$("#txtKBM01").val("Ctrl+S 在文本框中触发！");
	 *		alert("Ctrl+S 在文本框中触发！");					
	 * },{target : "txtKBM01"});
	 * @example
	 * //为文本框txtKBM02绑定两个快捷键
	 * KeyboardMapper.addShortcuts(["Ctrl+A","Ctrl+B"],function(){
	 *		$("#txtKBM02").val("Ctrl+A或Ctrl+B 在文本框中触发！");
	 *		alert("Ctrl+A或Ctrl+B 在文本框中触发！");					
	 * },{target : "txtKBM02"});
	 * 
	 */
	KeyboardMapper.addShortcuts = function(arrShortcuts,fnCallback,optConfig){
		//确保参数中的回调函数是一个function，否则当其为一个空函数
		fnCallback = typeof fnCallback === "function" ? fnCallback : function(){};
		
		//处理配置参数
		var defaultConfig = {
			target : document	//可以是DOM节点的ID，默认是document
		};
		if(optConfig){
			for(var property in optConfig){
				optConfig[property] ? defaultConfig[property] = optConfig[property] : null;
			}
		}
		
		//保存快捷键与回调函数之间的映射关系，便于后续处理
		if(typeof arrShortcuts === "string"){	//字符串
			arrShortcuts = arrShortcuts.toUpperCase();
			KeyboardMapper.KeyMaps.push({"key" : arrShortcuts,"fn" : fnCallback,"usability" : true});
			arrShortcuts = [arrShortcuts];
		}else if(typeof arrShortcuts === "object" && typeof arrShortcuts.length === "number"){	//数组
			for(var i = 0;i < arrShortcuts.length;i++){
				arrShortcuts[i] = arrShortcuts[i].toUpperCase();
				KeyboardMapper.KeyMaps.push({"key" : arrShortcuts[i],"fn" : fnCallback,"usability" : true});
			}
		}

		//获取所有自定义的快捷键组合
		var _createKeys = function(arr){
			var _allKeys = [];
			for(var i = 0;i < arr.length;i++){
				_allKeys.push(arr[i].split("+"));
			}
			return _allKeys;
		};
		var allKeys = _createKeys(arrShortcuts);
		
		//键盘按下的次数
		var keyPressedCounter = 0;
		
		//在这里控制：必须键盘上的快捷键是连续按下时候才触发，否则记录器清空
		var keyupListener = function(){
			allKeys = _createKeys(arrShortcuts)
			keyPressedCounter = 0; 
			return true;
		};
		
		//动作注册
		var KeyboardMapperListener = function(_event){
			//激活当前对象的keyPress事件
			this.onkeypress = function(){return true;};
			//FireFox中的event对象需要显式的给出
			var evt = _event || window.event;
			var keyCode = evt.keyCode || evt.which;
			var keyName = String.fromCharCode(keyCode);
			switch(keyCode){
				case 16 : // Shift
					keyName = "Shift";
					break;
				case 17 : // Ctrl
					keyName = "Ctrl";
					break;
				case 18 : // Alt
					keyName = "Alt";
					break;
			}
			
			//利用搜索删除规则，唯一定位快捷键
			for(var i = 0;i < allKeys.length;i++){
				if(allKeys[i][keyPressedCounter] != keyName.toUpperCase()){
					allKeys.splice(i,1);
					i--;
					if(allKeys.length == 0){
						allKeys = _createKeys(arrShortcuts);
						keyPressedCounter = 0;
						break;
					}
					continue;
				}else{
					//开始判断
					if(++keyPressedCounter == allKeys[i].length){
						try{
							for(var _sc = 0;_sc < KeyboardMapper.KeyMaps.length;_sc++){
								if(KeyboardMapper.KeyMaps[_sc]["key"].toUpperCase() == allKeys[i].join("+").toUpperCase()){
									//有效的阻止浏览器的功能键（如Ctrl+S）
									this.onkeypress = function(){return false;};
									this.onkeyup = function(){return false;};
									//执行自定义的方法
									KeyboardMapper.KeyMaps[_sc]["fn"].call(this);
									//阻止冒泡
									if(evt.stopPropagation){
										evt.stopPropagation();
										evt.preventDefault();
									}else{
										evt.cancelBubble = true;
										evt.returnValue = false;
									}
									allKeys = _createKeys(arrShortcuts);
									keyPressedCounter = 0;
									return false;
								}
							}
						}catch(_error){}
					}else if(keyPressedCounter > allKeys[i].length){
						allKeys = _createKeys(arrShortcuts)
						keyPressedCounter = 0;
					}
					break;
				}
			}
			return true;
		};
		
		//取得目标元素
		var targetElement = document.getElementById(defaultConfig["target"]) || document;
		
		//快捷键注册
		if(targetElement.addEventListener){
			targetElement.addEventListener("keydown", KeyboardMapperListener, false);
			targetElement.addEventListener("keyup", keyupListener, false);
		}else if(targetElement.attachEvent) {
			targetElement.attachEvent('onkeydown', KeyboardMapperListener);
			targetElement.attachEvent('onkeyup', keyupListener);
		}else {
			targetElement["onkeydown"] = KeyboardMapperListener;
			targetElement["onkeyup"] = keyupListener;
		}
		
		//alert("绑定快捷键成功！");
		return KeyboardMapper;
	};
	
	/**
	 * 禁用快捷键
	 * @param {String/Array} keyNames 本参数很活跃，可以是空、字符串、数组对每种情况分别解释如下：<br />
	 * 		1、参数为空：禁用所有快捷键<br />
	 * 		2、参数为字符串：禁用字符串所指定的快捷键，如"Ctrl+A"<br />
	 * 		3、参数为数组：禁用数组中的每一个快捷键，如["Ctrl+B","Ctrl+D"]<br />
	 * @return {object} KeyboardMapper本身
	 * @example
	 * //禁用单个快捷键
	 * KeyboardMapper.disableShortcuts("Ctrl+A");
	 * @example
	 * //禁用多个快捷键
	 * KeyboardMapper.disableShortcuts(["Ctrl+A","Ctrl+B","Ctrl+C"]);
	 * @example
	 * //禁用全部快捷键
	 * KeyboardMapper.disableShortcuts();
	 */
	KeyboardMapper.disableShortcuts = function(keyNames){
		
		function doOneKeyFn(keyName){
			if (KeyboardMapper.KeyMaps[i].key == keyName.toUpperCase()){
			    KeyboardMapper.KeyMaps[i].usability = false;
			    return true;
			}
		    return false;
		};
		
		if ( !keyNames ){
			//when keyNames is null,undefined or "",all the shortcuts will be disabled.
		    for ( i in KeyboardMapper.KeyMaps )
			    KeyboardMapper.KeyMaps[i].usability = false;
		}else{
			var keyArray = _doArgument(keyNames);
			_doManyKeys(keyArray, doOneKeyFn);
		}

		return KeyboardMapper;
	};
	
	/**
	 * 恢复（启用）快捷键
	 * @param {String/Array} keyNames 本参数很活跃，可以是空、字符串、数组对每种情况分别解释如下：<br />
	 * 		1、参数为空：恢复所有快捷键<br />
	 * 		2、参数为字符串：恢复字符串所指定的快捷键，如"Ctrl+A"<br />
	 * 		3、参数为数组：恢复数组中的每一个快捷键，如["Ctrl+B","Ctrl+D"]<br />
	 * @return {object} KeyboardMapper本身
	 * @example
	 * //恢复（启用）单个快捷键
	 * KeyboardMapper.restoreShortcuts("Ctrl+A");
	 * @example
	 * //恢复（启用）多个快捷键
	 * KeyboardMapper.restoreShortcuts(["Ctrl+A","Ctrl+B","Ctrl+C"]);
	 * @example
	 * //恢复（启用）全部快捷键
	 * KeyboardMapper.restoreShortcuts();
	 */
	KeyboardMapper.restoreShortcuts = function(keyNames){
	
		function doOneKeyFn(keyName){
			if (KeyboardMapper.KeyMaps[i].key == keyName.toUpperCase()){
		    	KeyboardMapper.KeyMaps[i].usability = true;
		    	return true;
		    }
		    return false;
		}
		
		if ( !keyNames ){
			//when keyNames is null,undefined or "",all the shortcuts will be disabled.
		    for ( i in KeyboardMapper.KeyMaps )
			    KeyboardMapper.KeyMaps[i].usability = true;
		}else{
			var keyArray = _doArgument(keyNames);
			_doManyKeys(keyArray, doOneKeyFn);
		}
		
		return KeyboardMapper;
	};
	
	/**
	 * 移除快捷键
	 * @param {String/Array} keyNames 本参数很活跃，可以是空、字符串、数组对每种情况分别解释如下：<br />
	 * 		1、参数为空：移除所有快捷键<br />
	 * 		2、参数为字符串：移除字符串所指定的快捷键，如"Ctrl+A"<br />
	 * 		3、参数为数组：移除数组中的每一个快捷键，如["Ctrl+B","Ctrl+D"]<br />
	 * @return {object} KeyboardMapper本身
	 * @example
	 * //移除单个快捷键
	 * KeyboardMapper.removeShortcuts("Ctrl+A");
	 * @example
	 * //移除多个快捷键
	 * KeyboardMapper.removeShortcuts(["Ctrl+A","Ctrl+B","Ctrl+C"]);
	 * @example
	 * //移除全部快捷键
	 * KeyboardMapper.removeShortcuts();
	 */
	KeyboardMapper.removeShortcuts = function(keyNames){

		function doOneKeyFn(keyName){
			if (KeyboardMapper.KeyMaps[i].key == keyName.toUpperCase()){
		    	KeyboardMapper.KeyMaps.splice(i,1);
		    	return true;
		    }
		    return false;
		}
		
		if ( !keyNames ){
			//when keyNames is null,undefined or "",clear the KeyMaps.
			KeyboardMapper.KeyMaps = [ ];
		}else{
			var keyArray = _doArgument(keyNames);
			_doManyKeys(keyArray, doOneKeyFn);
		}
		
		return KeyboardMapper;
	};
		
	/**
	 * 修改快捷键<br />
	 * 本方法的调用格式可以是如下两种：<br />
	 * 1、alterShortcuts("Ctrl+A",fnCallback);<br />
	 * 2、alterShortcuts(["Ctrl+A","Ctrl+B"],fnCallback);<br />
	 * @param {String/Array} keyNames 本参数可以是字符串，也可以是数组，分别解释如下：<br />
	 * 		1、字符串：修改字符串指定的快捷键，使其映射到fnCallback，如"Ctrl+A"<br />
	 * 		2、数组：修改数组中的每一个快捷键，使其都映射到fnCallback，如["Ctrl+A","Ctrl+B"]<br />
	 * @param {function} fnCallback 需要重新指定的回调函数
	 * @return {object} KeyboardMapper本身
	 * @example
	 * //修改一个快捷键
	 * KeyboardMapper.alterShortcuts("Ctrl+A",function(){
	 *		alert("Ctrl+A这个快捷键被修改咯！你只能按Ctrl+B了！" )
	 * });
	 * @example
	 * //修改多个快捷键
	 * KeyboardMapper.alterShortcuts(["Ctrl+A","Ctrl+B"],function(){
	 *		alert("Ctrl+A和Ctrl+B这个快捷键被修改咯！" )
	 * });
	 * 
	 */
	KeyboardMapper.alterShortcuts = function(keyNames,fnCallback){
		
		function doOneKeyFn(keyName, arg1){
			if(KeyboardMapper.KeyMaps[i]["key"].toUpperCase() == keyName.toUpperCase()){
				KeyboardMapper.KeyMaps[i]["fn"] = arg1;
				return true;
			}
		    return false;
		}
		
		//如果给定的参数不合法，则不修改任何快捷键
		if ( arguments.length < 2 || typeof fnCallback !== "function"){
			return this;
		}else{
			var keyArray = _doArgument(keyNames);
			_doManyKeys(keyArray, doOneKeyFn, fnCallback);
		}
		
		return KeyboardMapper;
	};
	
	/**
	 * 查询已经绑定的快捷键
	 * @param {String/Array} keyNames 本参数很活跃，可以是空、字符串、数组对每种情况分别解释如下：<br />
	 * 		1、参数为空：查询所有快捷键<br />
	 * 		2、参数为字符串：查询字符串所指定的快捷键，如"Ctrl+A"<br />
	 * 		3、参数为数组：查询数组中的每一个快捷键，如["Ctrl+B","Ctrl+D"]<br />
	 * @return {object/Array} 快捷键对象，即KeyboardMapper.KeyMaps数组中的对象
	 * @example
	 * //查找一个指定的快捷键
	 * var objSc = KeyboardMapper.findShortcuts("Ctrl+A");
	 * //查找多个快捷键
	 * var lstSc = KeyboardMapper.findShortcuts(["Ctrl+A","Ctrl+B","Ctrl+C"]);
	 */
	KeyboardMapper.findShortcuts = function(keyNames){
		
		var findedList = [];
		
		function doOneKeyFn(keyName, result){
			var fnFindedList = result;
			if (KeyboardMapper.KeyMaps[i].key == keyName.toUpperCase()){
		    	fnFindedList.push(KeyboardMapper.KeyMaps[i])
		    	return true;
		    }
		    return false;
		}

		if ( !keyNames ){
			//when keyNames is null,undefined or "",return all the KeyMaps.
		    findedList = KeyboardMapper.KeyMaps;
		}else{
			var keyArray = _doArgument(keyNames);
			_doManyKeys(keyArray, doOneKeyFn, findedList);
		}
				
		return findedList;
	};
	
	/**
	 * 处理KeyboardMapper.KeyMaps的单个元素
	 */
	var _doOneKey = function(keyName, fn, fnArgs){
		for ( i in KeyboardMapper.KeyMaps ){
            var isbreak = fn(keyName, fnArgs);
            if(isbreak) break;
		}
	}
	/**
	 * 处理KeyboardMapper.KeyMaps的多个元素
	 */
	var _doManyKeys = function(keyArray, fn, fnArgs){
		for ( i in keyArray){
			_doOneKey(keyArray[i], fn, fnArgs);
		}
	}
	/**
	 * 处理接口的参数 
	 */
	var _doArgument = function(keyNames){

		if (typeof keyNames === "string"){
			//when keyNames is string, single key.
			return [keyNames];
		}else if (typeof keyNames === "object" && typeof keyNames.length === "number"){
			//when keyNames is array, multi keys
			return keyNames;
		}
		
		return [ ];
	}
		
})();
